package com.csj.DesignPattern.spring.customSpring.framework.factory;

import cn.hutool.core.util.StrUtil;
import com.csj.DesignPattern.spring.customSpring.framework.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ClassPathXmlApplicationContext extends AbstractApplicationContext{

    public ClassPathXmlApplicationContext(String configLocation) {
        this.configLocation = configLocation;
        this.beanDefinitionReader = new XmlBeanDefinitionReader();
        try {
            refresh();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Object getBean(String name) throws Exception {
        Object object = singletonObjects.get(name);
        if (object != null) {
            return object;
        }
        BeanDefinitionRegistry registry =  beanDefinitionReader.getRegistry();
        BeanDefinition beanDefinition = registry.getBeanDefinition(name);
        Class<?> clazz = Class.forName(beanDefinition.getClassName());
        Constructor<?> constructor = clazz.getConstructor();
        Object bean = constructor.newInstance();

        MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues) {
            String propertyName = propertyValue.getName();
            String value = propertyValue.getValue();
            String ref = propertyValue.getRef();
            if (!StrUtil.isBlank(ref)) {
                // 被注入的对象（如userMapper）
                Object refBean = getBean(ref);
                String setterMethodName = getSetterMethodByFieldName(propertyName);
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    if (method.getName().equals(setterMethodName)) {
                        // bean表示需要依赖的对象（userService），refBean表示被依赖的对象（userMapper）
                        method.invoke(bean, refBean);
                    }
                }
            }
            if (!StrUtil.isBlank(value)) {
                String setterMethodName = getSetterMethodByFieldName(propertyName);
                Method method = clazz.getMethod(setterMethodName, String.class);
                method.invoke(bean, value);
            }
        }

        singletonObjects.put(name, bean);
        return bean;
    }

    private String getSetterMethodByFieldName(String fieldName) {
        String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
        return methodName;
    }

    @Override
    public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
        Object bean = getBean(name);
        if (bean == null) {
            return null;
        }
        return clazz.cast(bean);
    }
}
